package com.example.administrator.websocketdemo.socket;

import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.example.administrator.websocketdemo.message.HeartBeatMessage;
import com.example.administrator.websocketdemo.message.Message;
import com.example.administrator.websocketdemo.message.PushResponseMessage;
import com.example.administrator.websocketdemo.service.SocketService;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

/**
 * 长连接
 */
public class SocketClient extends WebSocketClient {
    private final String TAG = SocketService.class.getSimpleName();
    private final int INTERVAL = 15 * 1000;
    private int heartBeatCount; //心跳计数
    private Handler handler = new Handler();
    private SocketListener listener;
    //走登录接口成功后返回给我们的登录令牌
    private String token;

    public SocketClient(URI serverURI) {
        super(serverURI);
    }

    /**
     * 开启定时器
     */
    public void startTimer() {
        if (timeRunable != null)
            handler.post(timeRunable);
    }

    /**
     * 移除定时器
     */
    public void removeTimer() {
        if (timeRunable != null)
            handler.removeCallbacks(timeRunable);
    }

    /**
     * 长链接开启时的回调方法
     *
     * @param serverHandshake
     */
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        Log.i(TAG, "长链接已启动");
        //开启定时器
        startTimer();
    }

    /**
     * 长连接关闭时的回调
     *
     * @param code
     * @param reason
     * @param remote
     */
    @Override
    public void onClose(int code, String reason, boolean remote) {
        Log.i(TAG, "长链接关闭:" + reason);
    }

    /**
     * 长连接出现异常时的回调
     *
     * @param ex
     */
    @Override
    public void onError(Exception ex) {
        Log.i(TAG, "长链接出现异常:" + ex.getLocalizedMessage());
    }

    /**
     * 检查长连接是否断开的线程
     * （原理：
     * 1.客户端每隔15秒向服务端发送心跳报文，并累加心跳计数；
     * 2.服务端收到心跳报文后反向发送给客户端反馈报文，告知客户端我已收到消息；
     * 3.如果客户端收到心跳反馈报文时，将心跳计数置0；
     * 4.如果客户端长时间没有收到心跳反馈报文，一旦心跳计数超过3次，则判断为长连接已经断开，需要建立新的长连接）
     */
    private Runnable timeRunable = new Runnable() {

        @Override
        public void run() {
            //先移除上一次未完成的线程任务
            handler.removeCallbacks(timeRunable);

            //检查本地长连接是否已经关闭，关闭了重新打开连接
            if (isClosed()) {
                Log.i(TAG, "长链接已关闭，准备重新链接");
                if (listener != null)
                    listener.onDisconnected();
                return;
            }

            //检查服务端长链接是否已经断开
            if (heartBeatCount >= 3) { //3次心跳都没收到反馈报文，则重连
                Log.i(TAG, "长链接已断开，准备重新链接");
                if (listener != null)
                    listener.onDisconnected();
                return;
            }

            //心跳计数累计
            heartBeatCount++;
            //发送心跳报文
            sendHeartBeatMessage(token);
            //每隔15秒重新执行该线程
            handler.postDelayed(timeRunable, INTERVAL);
        }
    };

    /**
     * 收到长链接推送过来的消息
     *
     * @param message
     */
    @Override
    public void onMessage(String message) {
        if (TextUtils.isEmpty(message))
            return;

        try {
            Message msg = JSON.parseObject(message, Message.class);
            switch (msg.getType()) {
                case PUSH: //服务端推送过来报文
                    //处理服务端推送过来的报文
                    dealwithPushMessage(msg);
                    //推送反馈报文给服务端（告知服务端已收到消息）
                    sendPushResponseMessage(new PushResponseMessage("可以是和服务端约定好的json字符串"));
                    break;
                case HEART_BEAT: //心跳反馈报文
                    Log.i(TAG, "收到，心跳反馈报文");
                    heartBeatCount = 0; //心跳计数清0
                    break;
            }
        } catch (Exception e) {
            Log.i(TAG, "解析数据出现异常");
            e.printStackTrace();
        }
    }

    /**
     * 推送心跳报文
     *
     * @param key
     */
    private void sendHeartBeatMessage(String key) {
        try {
            HeartBeatMessage heartBeatMessage = new HeartBeatMessage(key);
            String json = JSON.toJSONString(heartBeatMessage);
            send(json); //发送心跳报文
            Log.i(TAG, "推送心跳报文：" + json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 推送反馈报文给服务端
     *
     * @param pushResponseMessage
     */
    private void sendPushResponseMessage(PushResponseMessage pushResponseMessage) {
        try {
            String json = JSON.toJSONString(pushResponseMessage);
            send(json); //发送推送反馈报文
            Log.i(TAG, "推送反馈报文给服务端：" + json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理服务端推送过来的消息
     *
     * @param message
     * @return
     */
    private void dealwithPushMessage(Message message) {
        //根据实际业务来处理
    }

    /**
     * 设置监听器
     *
     * @param listener
     */
    public void setSocketListener(SocketListener listener) {
        this.listener = listener;
    }

}